package com.btt.pattern.proxy.custom;


import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * created by Btt on 2018/07/03
 */

public class BTProxy {

    private static final String ln = "\r\n";

    public static Object newProxyInstance(BTClassLoader classLoader, Class<?>[] interfaces, BTInvocationHandler h){
        //1、动态生成源文件.java
        String src = generateSrc(interfaces);
        try {
        //2、java文件输出磁盘
            String filePath = BTProxy.class.getResource("").getPath();
            System.out.println(filePath);
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();

        //3、把生成的.java文件加载到JVM中来
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task =compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();

        //4、编译生成.class文件加载到JVM中
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor c = proxyClass.getConstructor(BTInvocationHandler.class);
            return c.newInstance(h);

        //5、返回字节码重组以后的新的代理对象
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    private static String generateSrc(Class<?>[] interfaces){

        StringBuffer sb = new StringBuffer();
        sb.append("package com.btt.pattern.proxy.custom;" + ln);
        sb.append("import com.btt.pattern.proxy.statics.Person;" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);

            sb.append("BTInvocationHandler h;" + ln);

            sb.append("public $Proxy0(BTInvocationHandler h){" + ln);

                sb.append("this.h = h;" + ln);

            sb.append("}" + ln);

            for (Method m : interfaces[0].getMethods()){
                sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);

                    sb.append("try{" + ln);

                        sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\", new Class[]{});" + ln);
                        sb.append("this.h.invoke(this, m, null);" + ln);
                    sb.append("}catch(Throwable e){" + ln);
                        sb.append("e.printStackTrace();" + ln);
                    sb.append("}" + ln);
                sb.append("}" + ln);
            }


        sb.append("}" + ln);

        return sb.toString();

    }

}
